package com.mijie.homi.ns.core;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mijie.core.ApplicationProperties;
import com.mijie.homi.common.domain.user.User;
import com.mijie.homi.common.domain.user.UserExtInfo;
import com.mijie.homi.common.protocol.HomiProtocolCodecFactory;
import com.mijie.homi.ns.domain.Constants;
import com.mijie.homi.ns.service.SessionService;
import com.mijie.homi.ns.service.UserService;
import com.mijie.util.DateUtil;
import com.mijie.util.MailUtil;

@Service
public class HomiServer {
	private  NioSocketAcceptor acceptor = null;
	public static final Charset CHARSET = Charset.forName("UTF-8");
	private static Logger logger = Logger.getLogger(HomiServer.class);
	private final int PORT = 9997;
	
	@Autowired
	private  UserService userService;
	@Autowired
	private  SessionService sessionService;
	@Autowired
	private HandlerDispatcher handler;
	
	public void start()  {
		if(acceptor==null){
			acceptor = new NioSocketAcceptor();
			String threadPoolSize = ApplicationProperties.get("threadPoolSize");
			if(threadPoolSize==null || threadPoolSize.trim().isEmpty()) threadPoolSize = "1024";
			Executor threadPool =  Executors.newFixedThreadPool(Integer.parseInt(threadPoolSize.trim())); 
			//codec必须在exector前,否则出现乱包.
			acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new HomiProtocolCodecFactory(CHARSET)));
			acceptor.getFilterChain().addLast("exector", new ExecutorFilter(threadPool));
			acceptor.setHandler(handler);
			acceptor.setReuseAddress(true);
			acceptor.setBacklog(128);
			SocketSessionConfig conf = acceptor.getSessionConfig();
			conf.setBothIdleTime(60*10);//timeout after 10 minutes 
			conf.setReadBufferSize(1024*5);
			conf.setReceiveBufferSize(1024*5);
			conf.setSendBufferSize(1024*5);
			conf.setKeepAlive(true);
			acceptor.setDefaultLocalAddress(new InetSocketAddress(PORT));
			try {
				acceptor.bind();
				onlineStat();
				userService.resetOnlineUserAmount();
				logger.info("!!!!!!!HomiServer start success!!!!!!!");
			} catch (IOException e) {
				e.printStackTrace();
				logger.error(e.getMessage(), e);
			}
			
			Runtime.getRuntime().addShutdownHook(new Thread(){
				@Override
				public void run() {
					shutdown();
				}
			});
		}
	}
	
	private void onlineStat(){
		new Thread(){
			public void run(){
				while(true){
					try {
						sleep(1000*60*Constants.LOGIN_LONG_STAT_INTERVAL_MINUTE);
					} catch (InterruptedException e) {
						logger.info(e.getMessage(), e);
					}
					Map<Long,IoSession> sessionMap = acceptor.getManagedSessions();
					Set<Long> keys = sessionMap.keySet();
					IoSession session = null;
					for(Long key : keys){
						session = sessionMap.get(key);
						incrOnlineLong(session,session.isConnected());
					}
				}
			}
		}.start();
	}
	
	public static void INFO(Object message){
		logger.info(message);
	}
	
	public static void ERROR(Object message){
		logger.error(message);
	}
	
	public static void ERROR(Object message,Throwable t){
		logger.error(message, t);
	}
	
	public IoSession getIoSessionById(Long sessionId){
		return acceptor.getManagedSessions().get(sessionId);
	}
	
	public  IoSession getIoSessionByUserId(int userId){
		IoSession session = null;
		long sessionId = sessionService.getIoSessionIdByUserId(userId);
		if(sessionId>0)
			session = getIoSessionById(sessionId);
		return session;
	}
	
	public  IoSession getIoSessionByToken(String token){
		IoSession session = null;
		long sessionId = sessionService.getIoSessionIdByToken(token);
		if(sessionId>0)
			session = getIoSessionById(sessionId);
		return session;
	}
	
	public  void incrOnlineLong(IoSession session,boolean online){
		Integer userId = null;
		Date recentCheckAt = null;
		Date cuttentTime = new Date();
		if(session!=null){
			userId = (Integer) session.getAttribute(Constants.KEY_USER_ID);
			recentCheckAt = (Date) session.getAttribute(Constants.KEY_ONLINE_MINUTE);
			if(userId==null || userId == 0 || recentCheckAt==null){
				return;
			}
			userService.incrOnlineLong(userId, DateUtil.getMinuteInterval(recentCheckAt, cuttentTime),online);
			session.setAttribute(Constants.KEY_ONLINE_MINUTE,cuttentTime);
		}
	}
	
	public static Integer getUserId(IoSession session){
		return (Integer) session.getAttribute(Constants.KEY_USER_ID);
	}
	
	private  Logger heartbeatLogger = Logger.getLogger("heartbeat");
	//登陆或者接收到心跳时记录
	public  void markUsed(IoSession session){
		String markAt = (String) session.getAttribute("markAt");
		if(markAt!=null && markAt.equals(DateUtil.getTodayStr())) return;
		session.setAttribute("markAt", DateUtil.getTodayStr());
		StringBuilder logContent = new StringBuilder();
		Integer userId = (Integer)session.getAttribute(Constants.KEY_USER_ID);
		User user = userService.getUserById(userId);
		UserExtInfo extInfo = userService.getUserExtInfoByUserId(userId);
		logContent.append("user:".intern()).append(user.getId()).append(" ");
		logContent.append("terminal:".intern()).append(extInfo.getTerminalId().trim().isEmpty()?"none".intern():extInfo.getTerminalId()).append(" ");
		logContent.append("gender:".intern()).append(user.getGender()).append(" ");
		logContent.append("os:".intern()).append(session.getAttribute(Constants.KEY_OS)).append(" ");
		logContent.append("appVer:".intern()).append(session.getAttribute(Constants.KEY_APPVER)).append(" ");
		logContent.append("province:".intern()).append(user.getProvince().trim().isEmpty()?"none".intern():user.getProvince()).append(" ");
		logContent.append("city:".intern()).append(user.getCity().trim().isEmpty()?"none".intern():user.getCity()).append(" ");
		logContent.append("userCreateAt:".intern()).append(DateUtil.date2str(user.getCreateAt(), false)).append(" ");
		logContent.append("time:".intern()).append(DateUtil.date2str(new Date(),true)).append(" ");
		heartbeatLogger.info(logContent.toString());
		logContent = null;
	}
		
	private void shutdown(){
		int numOfSession = acceptor.getManagedSessionCount();
		Map<Long,IoSession> sessionMap = acceptor.getManagedSessions();
		Set<Long> keys = sessionMap.keySet();
		IoSession session = null;
		int killCount = 0;
		Object userId = null;
		String token = null;
		for(Long key : keys){
			session = sessionMap.get(key);
			if(session!=null){
				userId = session.getAttribute(Constants.KEY_USER_ID);
				token = (String) session.getAttribute(Constants.KEY_TOKEN);
				if(userId==null) userId = "none";
				if(token==null) token = "none";
				logger.info("!!!!!!!HomiServer shutdown kicks user -> "+userId+",token ->"+token);
				session.close(false);
				killCount++;
			}
		}
		acceptor.unbind();
		acceptor.dispose();
		
		MailUtil mail = new MailUtil();
		mail.setHost("smtp.51homi.net");  
		mail.setUserName("server@51homi.net");  
		mail.setSenderName("服务器警报");
		mail.setPassWord("IAMmijie168");  
		mail.setTo("15876689000@139.com");  
		mail.setFrom("server@51homi.net");  
		mail.setSubject("哈米 notification server was shutdown!");  
		mail.setContent("The notification server was shutdown,please check the server if it is non-artificially."); 
		mail.sendMail();
		
		logger.info("!!!!!!!HomiServer shutdown[numOfSession:"+numOfSession+",kill:"+killCount+"]!!!!!!!");
	}

	public Map<Long,IoSession> getManagedSessions() {
		return  acceptor.getManagedSessions();
	}

	
}
